Google Apps Script で複数のPDFを結合できないかと探していたら、officeの杜 | PDFを結合するという記事がありました。
officeの杜で共有されているこちらのコード にある mergePdfs() を活用させていただきました。元ネタは、stack overflowにある Merge Multiple PDF's into one PDF です。
結合できるPDFファイル
stack overflowのコメントにPDFのバージョンが1.5以上である場合には大きな修正が必要(結合できない)と記載されていますが、Googleの機能(印刷)やChromeの印刷でPDFにしたPDFはマージ可能でした。
対象 | PDFバージョン | マージ可否 |
---|---|---|
Googleドキュメント | 1.4 | ◯ |
Googleプレゼンテーション | 1.7 | ◯ |
Googleスプレットシート | 1.7 | ◯ |
Google Chrome | 1.4 | ◯ |
Office 365 | 1.7 | ✕ |
PDFのバージョンはAdobe Acrobat Readerで確認できます。
下記は、Googleドキュメントの印刷からPDFを保存した場合です。バージョンは1.4となっています。
Googleスプレッドシート、プレゼンテーションは、バージョンが1.7と表示されますが、
前述したように問題なく結合することができます。
Office 365でPDFに変換したファイルは結合できませんでしたが、Googleを中心としたサービスを利用しているのであれば問題なさそうです。
mergePdfs()関数を使う
mergePdfs()関数の説明と引数をみると
drirectory | PDFが格納されているディレクトリID |
name | 結合したPDFのファイル名 |
pdf1, ... | 結合するPDFファイル |
結合するPDFファイルは2つ以上複数指定できますが、引数でファイル名をひとつづつ指定するのは煩わしい。
ということで、フォルダーにあるPDFをリスト(配列に格納)して引数として渡せるように変更してみました。
またPDFファイルの結合順番はファイル名で昇順にソートした順番にします。ファイル名の先頭に数字を入れておけば番号順で結合されます。
例えば、ファイル名を
0.test.pdf
1.test.pdf
2.test.pdf
としておけば、0.test、1.test、2.testの順番で結合されます。
mergePDFs()関数の修正
先ずは、office杜のこちらのリンクからmergePdfs()のプログラムを取得してください。
修正箇所は3箇所です。
12行目
関数の引数をファイル名から、配列を1つ渡すように変更します。
function mergePdfs(directory, name, pdf1, pdf2, opt_pdf3) {
function mergePdfs(directory, name, pdfList {
21行目
関数の引数ループで取得しているところを、引数に格納されているファイル数分(配列の長さ分)で処理するように変更します。
for (var argumentIndex = 2; argumentIndex < arguments.length; argumentIndex++) {
for (var i=0; i<pdfList.length; i++) {
23行目
マージするPDFのファイルサイズ取得を引数の配列からに変更します。
var bytes = arguments[argumentIndex].getBlob().getBytes()
var bytes = pdfList[i].getBlob().getBytes()
また、475行目移行はPDFを分割する関数のコードになっていますので削除してしまっても問題ありません。
mergePDFs()関数を呼び出す
次は、修正したmergePDFs()関数を呼び出すプログラムになります。指定したフォルダIDにあるPDFファイルを配列(pdfList)に格納して、ファイル名順にソートしてから mergePDFsに引き渡します。
<フォルダID>にはGoogleドライブのフォルダIDを、<ファイル名>には結合して作成するファイル名にしてください。
フォルダIDは、フォルダを開いたときのURL、https://drive.google.com/drive/folders/XXXXX のXXXX部分になります。
function merge(srcFolderId, fileName){ var srcFolderId = '<フォルダID>' //フォルダ ID var fileName = '<ファイル名>' //結合後のファイル名 //フォルダ内のファイルを取得 var srcFolder = DriveApp.getFolderById(srcFolderId) var srcFiles = srcFolder.getFiles() //PDFファイルだけを配列に格納 var pdfList = [] while(srcFiles.hasNext()) { var srcFile = srcFiles.next() if (srcFile.getMimeType()==='application/pdf') { pdfList.push(srcFile); } } pdfList.sort() //照準でソート mergePdfs(srcFolder, fileName, pdfList) }
13行目のgetMimeType()でPDFファイルだけを対象にしています。また17行目のsort()で配列に格納した各ファイルをファイル名でソートしています。
スプレッドシートのUI機能で汎用的にする
もう少し汎用的に使えるように、スプレッドシートのUI機能を使って、フォルダIDと結合して作成するファイル名をUI上で入力するようにします。
またスプレッドシートのメニューからGASの実行を可能にします。
スプレッドシートの機能を使うので、Googleスプレッドシートの拡張機能から Apps Script を作成する必要があります。
スプレッドシートにメニューに追加する
スプレッドシートが開かれたときにメニューを追加します。こんな感じです。
「mergePDFs」 > 「PDFを結合」 で diaLog関数を実行します。
function onOpen(){ SpreadsheetApp .getActiveSpreadsheet() .addMenu('mergePDFs', [ {name: 'PDFを結合', functionName: 'diaLog'} ]) }
ダイアログ
詳細の説明は省きますが、「フォルダーID」と「ファイル名」の入力の後に確認画面を出してが簡単なチェックと確認を出力して merge()関数にフォルダIDとファイル名を引き渡します。
またPDFの結合でエラーとなった場合にもエラー出力をするようにしています。
下記がサンプルコードとなります。
function diaLog() { /******************** PDFがあるフォルダを指定 ********************/ var srcFolderId = Browser.inputBox("結合したいPDFがあるフォルダを指定してください(FolderID)",Browser.Buttons.OK_CANCEL) //キャンセルが押下されたら終了 if (srcFolderId == 'cancel') { return } try { var srcFolder = DriveApp.getFolderById(srcFolderId) } catch(e) { Browser.msgBox('ERROR', srcFolderId + ' is not available', Browser.Buttons.OK) return } /******************** ファイル名の指定 ********************/ var fileName = Browser.inputBox("結合後のファイル名を指定してください(FolderID)",Browser.Buttons.OK_CANCEL) //キャンセルが押下されたら終了 if (fileName == 'cancel') { return } else if (fileName == '') { Browser.msgBox('ERROR', 'File name is not defined', Browser.Buttons.OK) return } /******************** 開始の確認 ********************/ var srcFolderName = srcFolder.getName() var result = Browser.msgBox('Confirm', '「' + srcFolderName + '」にあるすべてのPDFを\\n「' + fileName + '」として結合します', Browser.Buttons.OK_CANCEL) if (result == 'cancel') { return } try { merge(srcFolderId, fileName) //PDF格納フォルダ,ファイル名 } catch(e) { Browser.msgBox('ERROR', 'Error Occurred during merging', Browser.Buttons.OK) return } Browser.msgBox('Complete', 'PDFの結合が完了しました', Browser.Buttons.OK) }
これで完成です。
スプレッドシートのメニューから dialog()関数を呼び出し、merge()関数でフォルダ内のPDFをリスト化して、mergePDFs()関数でPDFを結合します。
先人の知恵と努力に感謝です。
GASの基礎を学べる参考図書
ある程度プログラミンがわかっていれば、WEBやYoutubeでも十分に調べられると思いますが、初歩的なところからであれば参考図書は有効な学習手段です。
「詳解! Google Apps Script完全入門 [第3版]」は、プラグラミング初心者にわかりやすく説明されています。GASを最初に学ぶ一冊として良書です。
Udemy オススメ講座
【新IDE対応】Google Apps Script(GAS)の基礎を完全習得
講師:事務職たらこ
印象に残りやすい手書き風スライドを用いGASの基本的なプログラミングを気軽に学ぶことができる。本講座でGASを活用した自動化ができるレベルにはなれないが、基礎としては十分。